#include "trap.h"
#include "types.h"
#include "riscv.h" // csrr()
#include "clock.h" // set_next_timer()
#include "proc.h" // proc_switch()
#include "panic.h"
#include "stdio.h" // puts()

void trap_init() {
	uint64 temp;
	csrw(stvec, trap_entry);
	// can't generate s-mode interrupt/exception in s-mode
	csrr(sstatus, temp);
	temp &= ~SSTATUS_SIE;
	csrw(sstatus, temp);
}

/* @brief:  trap dispatcher
 */
void trap(struct trapframe *tf_ptr) {
	uint64 type = 0;
	csrr(scause, type);
	if (type & (1ULL << 63)) {
		// interrupts, the 63th bit is 1
		// clean the 1 at the 63th bit
		type = (type << 1) >> 1;
		switch(type) {
			case S_TIMER:
				set_next_timer();
				puts("Timer interrupt.\n");
				proc_switch();
				trap_ret_user();
				break;
			default:
				panic("trap: unexcepted interrupt.\n");
		}

	} else {
		// exceptions, the 63th bit is 0
		switch(type) {
			case U_ENV_CALL:
				// when returning to u mode
				// jump to the next instruction of causing the trap
				tf_ptr->epc += 4;
				syscall(tf_ptr);
				trap_ret_user();
				break;
			default:
				debug_uint("exception type", type);
				panic("trap: unexcepted exception.\n");
		}
	}

}

/* @brief: return to user mode from a trap
 */
void trap_ret_user() {
	struct trapframe *tf_ptr = cur_proc->tf_ptr;
	uint64 temp = 0;
	// restore sepc
	csrw(sepc, tf_ptr->epc);
	// set sstatus spp to 0, return to user mode after "sret"
	csrr(sstatus, temp);
	temp &= ~SSTATUS_SPP;
	csrw(sstatus, temp);
	trap_end(tf_ptr);
}
